//
//  BluetoothModels.swift
//  Nexs
//
//  Created by roni on 2025/7/28.
//

import Foundation
import CoreBluetooth

// MARK: - 蓝牙设备信息封装
public class BluetoothDevice {
    public let peripheral: CBPeripheral
    public let advertisementData: [String: Any]
    public let rssi: NSNumber
    public let name: String
    public var deviceServicesMapArray: [String: [String]] = [:]
    public private(set) var characteristics: [CBCharacteristic] = []
    
    public var mac: String = "" // mac
    public var bleMac: String = "" // ble mac
    public var sn: String = "" // sn
    public var batteryLevel: Int = 0 // 电量
    public var isCharging: Bool = false // 是否正在充电
    public var version: String = "" // 版本， 类似，1.1.1.3
    public var hexVersion: String = "" // 版本16 进制
    public private(set) var connectionStrength: Int  = 3 // 1-3 信号强度
    public private(set) var connectionState: DeviceConnectionState = .disconnected
    
    public var serviceIds: [String] {
        return deviceServicesMapArray.keys.map({ "\($0)" })
    }
    
    public var characteristicIds: [String] {
        return deviceServicesMapArray.values.flatMap({ $0 })
    }
    
    public var deviceId: UUID {
        peripheral.identifier
    }
    
    public var deviceIdString: String {
        peripheral.identifier.uuidString
    }
    
    /// 获取指定服务的特征值 ids
    /// - Parameter serviceId: 服务 id
    /// - Returns: 特征值 ids
    public func getCharacteristicIdsInService(_ serviceId: String) -> [String]? {
        guard let item = deviceServicesMapArray[serviceId] else {
            return nil
        }
        
        return item
    }
    
    public func getCharacteristic(_ id: String) -> CBCharacteristic? {
        guard let item = characteristics.first(where: { $0.uuid.uuidString == id }) else {
            return nil
        }
        
        return item
    }
    
    public init(peripheral: CBPeripheral, advertisementData: [String: Any], rssi: NSNumber) {
        self.peripheral = peripheral
        self.advertisementData = advertisementData
        self.rssi = rssi
        self.name = peripheral.name ?? ""
    }
    
    public func updateCharacteristics(_ values: [CBCharacteristic]) {
        self.characteristics = values
    }
    
    public func addCharacteristics(_ values: [CBCharacteristic]) {
        self.characteristics.append(contentsOf: values)
    }
    
    public func updateConnectionState(state: DeviceConnectionState) {
        self.connectionState = state
    }
}

// MARK: - 连接策略枚举
public enum ConnectionStrategy {
    case first          // 连接第一个扫描到的设备
    case last           // 连接最后一个扫描到的设备
    case custom((BluetoothDevice) -> Bool)  // 自定义连接规则
    case connectWhenDiscovery((BluetoothDevice) -> Bool) // 发现满足的设备就连接并停止扫描
}

// MARK: - 蓝牙状态枚举
public enum BluetoothConnectionState {
    case idle           // 空闲状态
    case scanning       // 扫描中
    case connecting     // 连接中
    case connected      // 已连接
    case disconnected   // 已断开连接
    case failed         // 连接失败
}

// MARK: - 设备连接状态
public enum DeviceConnectionState {
    case disconnected   // 未连接
    case connecting     // 连接中
    case connected      // 已连接
    case failed         // 连接失败
}

// MARK: - 连接的设备信息
public class ConnectedBluetoothDevice {
    public var device: BluetoothDevice
    public let connectionState: DeviceConnectionState
    public let connectedAt: Date
    
    public init(device: BluetoothDevice, connectionState: DeviceConnectionState, connectedAt: Date = Date()) {
        self.device = device
        self.connectionState = connectionState
        self.connectedAt = connectedAt
    }
    
    var deviceId: UUID {
        device.peripheral.identifier
    }
}

// MARK: - 蓝牙配置
public struct BluetoothConfiguration {
    /// 唯一标识符，用于生成队列
    public let identifier: String
    
    /// 要扫描的服务UUID列表，nil表示扫描所有设备
    /// 指定特定的服务UUID可以提高扫描效率和精确度
    public let serviceUUIDs: [CBUUID]?
    
    // 设备对象对应的服务和服务对应的特征值, 形如： [1: ["a", "b"], 2: ["c", "d"]]
    public let deviceServicesMapArray: [String: [String]]
    
    /// 扫描超时时间（秒），默认10秒, 0 秒代表不会超时
    /// 超过此时间后会停止扫描并返回已发现的设备
    public let scanTimeout: TimeInterval
    
    /// 连接超时时间（秒），默认15秒, 0 秒代表不会超时
    /// 超过此时间连接尝试将被取消
    public let connectionTimeout: TimeInterval
    
    /// 是否允许重复发现同一设备，默认false
    /// 设为true会在设备广播时重复触发发现回调
    public let allowDuplicates: Bool
    
    /// 连接策略，定义如何选择要连接的设备
    /// .first: 连接第一个发现的设备
    /// .last: 连接最后一个发现的设备
    /// .custom: 使用自定义规则选择设备
    public let connectionStrategy: ConnectionStrategy
    
    /// 最大并发连接数，默认1个
    /// 设置为大于1的值可以同时连接多个蓝牙设备
    public let maxConcurrentConnections: Int
    
    /// 是否启用自动重连，默认false
    /// 启用后设备意外断开时会尝试自动重新连接
    public let autoReconnect: Bool
    
    /// 日志接口
    public let logger: BluetoothLogable
    
    // 设备过滤规则自定义，发现设备后，满足规则才会被添加进而连接
    public var filter: ((_ device: CBPeripheral, _ advertisementData: [String: Any], _ RSSI: NSNumber) -> Bool)?
    
    /// 初始化蓝牙配置
    /// - Parameters:
    ///   - identifier: 唯一标识，用于生成队列
    ///   - serviceUUIDs: 要扫描的服务UUID字符串数组，nil表示扫描所有设备
    ///   - deviceServicesMapArray:设备对象对应的服务和服务对应的特征值, 用于订阅服务和特征值
    ///   - scanTimeout: 扫描超时时间（秒），默认10秒
    ///   - connectionTimeout: 连接超时时间（秒），默认15秒
    ///   - allowDuplicates: 是否允许重复发现同一设备，默认false
    ///   - connectionStrategy: 连接策略，默认连接第一个设备
    ///   - maxConcurrentConnections: 最大并发连接数，默认1个，最小值为1
    ///   - autoReconnect: 是否启用自动重连，默认false
    ///   - logger: BluetoothLogger 默认实现
    public init(identifier: String = "com.nexs.bluetooth.central",
                serviceUUIDs: [String]? = nil,
                deviceServicesMapArray: [String: [String]] = [:],
                scanTimeout: TimeInterval = 10.0,
                connectionTimeout: TimeInterval = 15.0,
                allowDuplicates: Bool = false,
                connectionStrategy: ConnectionStrategy = .first,
                maxConcurrentConnections: Int = 1,
                autoReconnect: Bool = false,
                logger: BluetoothLogable = BluetoothCentralLogger()) {
        self.identifier = identifier
        self.serviceUUIDs = serviceUUIDs?.map { CBUUID(string: $0) }
        self.deviceServicesMapArray = deviceServicesMapArray
        self.scanTimeout = scanTimeout
        self.connectionTimeout = connectionTimeout
        self.allowDuplicates = allowDuplicates
        self.connectionStrategy = connectionStrategy
        self.maxConcurrentConnections = max(1, maxConcurrentConnections) // 至少支持1个连接
        self.autoReconnect = autoReconnect
        self.logger = logger
    }
}

// MARK: - 错误类型定义
public enum BluetoothError: Error, LocalizedError {
    case bluetoothUnavailable           // 蓝牙不可用
    case powerOff                       // 蓝牙已关
    case scanTimeout                    // 扫描超时
    case connectionTimeout              // 连接超时
    case connecting                     // 连接中
    case connectionFailed(Error?)       // 连接失败
    case noDeviceFound                  // 未找到设备
    case alreadyConnected              // 已经连接
    case notConnected                  // 未连接
    case serviceNotFound               // 服务未找到
    case characteristicNotFound        // 特征值未找到
    case writeError(Error?)            // 写入错误
    case readError(Error?)             // 读取错误
    case maxConnectionsReached         // 已达到最大连接数
    case deviceNotFound                // 设备未找到
    case invalidDevice                 // 无效设备
    case readRSSIError(Error?)         // 读取错误
    
    public var errorDescription: String? {
        switch self {
        case .bluetoothUnavailable:
            return "蓝牙不可用"
        case .powerOff:
            return "蓝牙未打开"
        case .scanTimeout:
            return "扫描超时"
        case .connectionTimeout:
            return "连接超时"
        case .connecting:
            return "有设备连接中"
        case .connectionFailed(let error):
            return "连接失败: \(error?.localizedDescription ?? "未知错误")"
        case .noDeviceFound:
            return "未找到设备"
        case .alreadyConnected:
            return "设备已经连接"
        case .notConnected:
            return "设备未连接"
        case .serviceNotFound:
            return "未找到指定服务"
        case .characteristicNotFound:
            return "未找到指定特征值"
        case .writeError(let error):
            return "写入错误: \(error?.localizedDescription ?? "未知错误")"
        case .readError(let error):
            return "读取错误: \(error?.localizedDescription ?? "未知错误")"
        case .maxConnectionsReached:
            return "已达到最大连接数"
        case .deviceNotFound:
            return "设备未找到"
        case .invalidDevice:
            return "无效设备"
        case .readRSSIError(let error):
            return "读取RSSI错误: \(error?.localizedDescription ?? "未知错误")"
        }
    }
}
